﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Text;
using System.Net;


namespace Loong.Net
{
    public class CookieManager
    {
        List<String> cookies;

        /// <summary>
        /// Creates a new cookie manager with no cookies.
        /// </summary>
        public CookieManager()
        {
            this.cookies = new List<String>();
        }

        /// <summary>
        /// Creates a new cookie manager and copies old cookies from another.
        /// </summary>
        /// <param name="source">Coookie manager to copy cookies from.</param>
        public CookieManager(CookieManager source)
        {
            this.cookies = new List<String>(source.cookies);
        }

        private void AddCookie(String cookie)
        {
            String key = cookie.Substring(0, cookie.IndexOf('='));
            foreach (String c in this.cookies)
            {
                String k = c.Substring(0, c.IndexOf('='));
                if (k == key)
                {
                    this.cookies.Remove(cookie);
                    break;
                }
            }
            this.cookies.Add(cookie);
        }

        /// <summary>
        /// Adds the cookies from a given response to the manager.
        /// </summary>
        public void GetCookies(HttpWebResponse response)
        {
            lock (this.cookies)
            {
                String[] cookies = response.Headers.GetValues("Set-Cookie");
                if (cookies != null)
                {
                    String lastCookie = "";
                    foreach (String cookie in cookies)
                    {
                        if (cookie.IndexOf('=') > cookie.IndexOf(';'))
                        {
                            // Some .net frameworks have a bug where they split
                            // some headers in two. Never mind, stick the headers
                            // back using the lastCookie variable.
                            lastCookie += cookie;
                        }
                        else
                        {
                            if (lastCookie.Length > 0)
                            {
                                AddCookie(lastCookie);
                            }
                            lastCookie = cookie;
                        }
                    }

                    if (lastCookie.Length > 0)
                    {
                        AddCookie(lastCookie);
                    }
                }
            }
        }

        /// <summary>
        /// Adds the cookies of the manager to the request.
        /// </summary>
        public void SetCookies(HttpWebRequest request)
        {
            lock (this.cookies)
            {
                StringBuilder cookies = new StringBuilder();
                foreach (String cookie in this.cookies)
                {
                    String c = null;
                    String domain = "";
                    String path = "";
                    DateTime expires = DateTime.MaxValue;
                    foreach (String part in cookie.Split(';'))
                    {
                        if (c == null)
                        {
                            c = part;
                        }
                        String[] parts = part.Trim().Split('=');
                        if (parts.Length == 2)
                        {
                            if (parts[0] == "domain")
                            {
                                domain = parts[1];
                            }
                            else if (parts[0] == "path")
                            {
                                path = parts[1];
                            }
                            else if (parts[0] == "expires")
                            {
                                expires = DateTime.Parse(parts[1], CultureInfo.InvariantCulture);
                            }
                        }
                    }
                    if (c == null)
                    {
                        break;
                    }
                    if (DateTime.Now > expires)
                    {
                        break;
                    }
                    if (!request.RequestUri.Host.EndsWith(domain))
                    {
                        break;
                    }
                    if (!request.RequestUri.PathAndQuery.StartsWith(path))
                    {
                        break;
                    }
                    if (cookies.Length > 0)
                    {
                        cookies.Append("; ");
                    }
                    cookies.Append(c);
                }
                if (cookies.Length > 0)
                {
                    request.Headers["Cookie"] = cookies.ToString();
                }
            }
        }

    }
}
